//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <PowerManager.h>

Boolean bAppStart = FALSE;

static PowerManager powerManager;

EXTERN_C void PCFOnkey1sOffEnable(Boolean IsEnable);
EXTERN_C void PCFWatchDogEnable(Boolean IsEnable);
EXTERN_C void PCFWatchDogReset();
EXTERN_C void soft_connect_hook( int enable );
void resetOOCC2();
void DoSpConfig();
void PoSetAutoWakeInterval(UInt32 Seconds);
void AudioWake();

void PoShutdown()
{
    //Disable usb connect
    DzDisableIrq(11);
    soft_connect_hook(0);
    setWatchDog(8*1000);
    pcf50606_power_off();
    return ;
}

ECode PowerManager::Read(
        /* [in] */ Int64 u64Offset,
       /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode PowerManager::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode PowerManager::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    //cprintf("+++++++++PowerManager::Control %d\n", nControlCode);
    switch(nControlCode)
    {
        //0-49,for ntos compliance
//        case 0:
//        {//means SetMode
//            UInt32 * t=(UInt32*)(char*)inBuffer;
//            int * task_id=(int*)t;
//            UInt32 *mode=++t;
//            if(Power_SetCpuMode(*task_id,*mode)<0)
//            {
//                 goto Exit;
//            }
//            break;
//        }
//        case 1:
//        {//means SetPolicy
//            UInt32 * t=(UInt32*)(char*)inBuffer;
//            int * task_id=(int*)t;
//            UInt32 *policy_type=++t;
//            if(Power_SetPolicy(*task_id,*policy_type)<0)
//            {
//                goto Exit;
//            }
//            break;
//        }
//        case 2:
//        {//means InitDevPowerMode
//            UInt32 * t=(UInt32*)(char*)inBuffer;
//            int task_id=(int)t[0];
//            CVirtualDevice *pvd=(CVirtualDevice *)t[1];
//            UInt32 DevMode=t[2];
//            UpdateTaskDevMode(task_id,pvd,DevMode);
//            break;
//        }
//        case 3:
//        {//means KeepSpecifiedDevMode
//            UInt32 * t=(UInt32*)(char*)inBuffer;
//            int task_id=(int)t[0];
//            CVirtualDevice *pvd=(CVirtualDevice *)t[1];
//            UInt32 DevMode=t[2];
//            Boolean KeepMode=(Boolean)t[3];
//            UpdateTaskKeepMode(task_id,pvd,DevMode,KeepMode);
//            break;
//        }
//        case 4:
//        {//means SetChangeModeInterval
//            UInt32 * t=(UInt32*)(char*)inBuffer;
//            int task_id=(int)t[0];
//            CVirtualDevice *pvd=(CVirtualDevice *)t[1];
//            UInt32 Interval=t[2];
//            UpdateTaskChangeInterval(task_id,pvd,Interval);
//            break;
//        }
//        case 10:
//        {//means GetMode
//            UInt32 mode=Power_GetCpuMode();
//            assert(pOutBuffer.Capacity () >= (int)sizeof(UInt32));
//            pOutBuffer.Copy(&mode, (int)sizeof(UInt32));
//            break;
//        }
//        case 11:
//        {//means GetPolicy
//            UInt32 policy=Power_GetPolicy();
//            assert(pOutBuffer.Capacity () >= (int)sizeof(UInt32));
//            pOutBuffer.Copy(&policy, (int)sizeof(UInt32));
//            break;
//        }

        //50-99,for test
        case 50:
            ShowDeviceStatus();
            break;

        //100-999,for power manager api
        case 100: {/*SetLCDPolicy*/
            assert(NULL != inBuffer.GetPayload());
            int nInterval=*(int *)inBuffer.GetPayload();
            NotifyByIsr(EVENT_POWER,(int)PoEventHandler,(void *)nInterval);
            break;
        }
        case 102: {/*GetLcdPowerPolicy 0:normal 1:always on*/
            assert(pOutBuffer && NULL != pOutBuffer->GetPayload());
            unsigned long uPolicy = PoGetLcdPowerPolicy();
            pOutBuffer->Copy((Byte *)&uPolicy, sizeof(unsigned long));
            break;
        }
        case 103: {/*PoLockScreen*/
            assert(NULL != inBuffer.GetPayload());
            bool bLock = *(bool *)inBuffer.GetPayload();
            PoSetCpuPowerOn((Int32)!bLock);
            break;
        }
        case 104: {/*PoGetBootStatus*/
            assert(pOutBuffer && NULL != pOutBuffer->GetPayload());
            unsigned long status = PoGetBootStatus();
            pOutBuffer->Copy((Byte *)&status,sizeof(unsigned long));
            break;
        }
        case 105: {/*PoReboot*/
            assert(NULL != inBuffer.GetPayload());
            int nInterval=*(int *)inBuffer.GetPayload();
            setWatchDog(nInterval);
            break;
        }
        case 106: {/*PoShutdown*/
            PoShutdown();
            break;
        }
        case 107: {/*PoSetAutoWakeInterval*/
            assert(NULL != inBuffer.GetPayload());
            UInt32 uInterval = *(UInt32 *)inBuffer.GetPayload();
            PoSetAutoWakeInterval(uInterval);
            break;
        }
        case 205: {/*Disable Onkey1S Shutdown*/
            PCFOnkey1sOffEnable(FALSE);
            break;
        }
        case 206: {/*Enable Onkey1S Shutdown*/
            PCFOnkey1sOffEnable(TRUE);
            break;
        }
        case 207:{//PCF 8S_WATCHDOG OFF:
            PCFWatchDogEnable(FALSE);
            PCFWatchDogReset();
            break;
        }

        case 208:{//PoSetBtPowerPolicy
            assert(NULL != inBuffer.GetPayload());
            int nInterval=*(int *)inBuffer.GetPayload();
            NotifyByIsr(EVENT_POWER,(int)PoSetBtPowerPolicy,(void *)nInterval);
            break;
        }

        default:
            break;
    }
    return NOERROR;
}

void pm_do_idle();
void CpuIdleWakeupCallback(int eType, int nCode, void *pParam);
void EnterIdleModeCallback(int eType, int nCode, void *pParam)
{
    pm_do_idle();
    return ;
}

void PoSetLcdLockScreen(Boolean Lock);
void PenEnable(Boolean bIsEnable);
void AutoSleepCallback(int eType, int nCode, void *pParam);

void LockScreenCallback(int eType, int nCode, void *pParam)
{//Lock notify from shellapp
    Boolean bLock = (Boolean)nCode;
    cprintf("\n=========== %s Screen ===========\n", bLock?"Lock":"Unlock");


    PoSetLcdLockScreen(bLock);//save lock screen status
    PCFOnkey1sOffEnable(!bLock);
    PoSetCpuPowerOn(!bLock);
    PenEnable(!bLock);

    if (bLock) {
        NotifyByIsr(EVENT_INPUT, 0x80, NULL);
    }
    else {
        NotifyByIsr(EVENT_INPUT, 0x81, NULL);
    }

    return ;
}

void PoDoMachineSleep(Boolean Sleep);

void AutoSleepCallback(int eType, int nCode, void *pParam)
{//sleep automatically
    cprintf("\n===========Auto Sleep===========\n");

    EmIsrParameter Param;
    memset(&Param,0,sizeof(Param));
    EmPostEventByIsr(213, &Param);//notify apps auto lock screen event

    //LNOTENOTE: Auto sleep only use in shellapp
    PoDoMachineSleep(TRUE);//CPU can Sleep when LCD PoweOff though not locked
}

extern int PowerWakeState;

void PoNotifyEvent(int evNum, void *pParameter)
{
    if (!bAppStart) return ;

    if (1 != PowerWakeState) EmPostEventByIsr(evNum, (EmIsrParameter*)pParameter);
    else {//Async
        cputs(">");
        unsigned long buf[1+sizeof(EmIsrParameter)/4];
        buf[0] = evNum;
        memcpy(&buf[1], pParameter, sizeof(EmIsrParameter));
        NotifyByIsr(EVENT_COMMON, 0, buf);
    }
}

void PoPostAllEvent()
{
    for(;;) {
        MemoryBuf_<20> ebbData;
        ECode ec = g_pEventQueues->Read(0x80000000|EVENT_COMMON, 0,
                &ebbData, NULL);
        if (FAILED(ec)) {
            cprintf("Events post ok.\n\n");
            break;
        }
        cputs("<");
        unsigned long * buf=(unsigned long *)ebbData.GetPayload();
        EmPostEventByIsr(buf[0], (EmIsrParameter*)&buf[1]);
    }
}

void DoSpConfig(void *pArg)
{
    if (bAppStart) return ;
    bAppStart = TRUE;//BUGBUG: Temp notify if smartphone apps  to be run

#if defined(_DBGSCHIRQ)
    kprintf("\n\nNow is running shellapp\n\n");
#endif

    DzSleep(30, NULL);

    //Add your code when smartphone app to be run
    AddNotify(EVENT_AUTO_SLEEP,(void *)AutoSleepCallback);//for auto sleep

    //dummy wake ac97 once time
    AudioWake();
    return ;
}

#if defined(_DBGSCHIRQ)
#include <bulverde.h>
EXTERN_C UInt32 save_flags_cli();
EXTERN_C void restore_flags(UInt32);
void prcdump();
void pcf50606_power_off(void);

static int Delay(unsigned long uSec)
{
    unsigned long time = OSCR;
    unsigned long expireTime = time + (uSec * 3);   // approx 3 ticks per uS;

    //
    // Check if we wrapped on the expireTime
    // and delay first part until wrap
    //
    if (expireTime < time)
    {
        while (time < OSCR) {
            if (STLSR & LSR_DR) break;
        }
    }

    while (OSCR <= expireTime){
            if (STLSR & LSR_DR) break;
    }

    return (STLSR & LSR_DR);
}

int bChatLoop = 1;
static void DebugCallback(int eType, int nCode, void *pParam)
{
    cprintf("pcf50606_power_off() = %x\n", pcf50606_power_off);
    cprintf("bChatLoop (%x)=%x\n", &bChatLoop, bChatLoop);
    //prcdump();
    bChatLoop = 1;

for(;bChatLoop;) {
    UInt32 flags = save_flags_cli();
    int period = 5 * 1000 * 1000;
    int count = 2;
    cprintf("nCode %d, pContext %08x\n", nCode, pParam);

    if (nCode) {//
        cprintf("Kernel debugger!\n");
        period *= 2;
    }
    else {
        cprintf("User debugger!\n");
    }

    while(STLSR & LSR_DR) {
        STRBR;
    }

    while(count--) {
        GPSR2 |= 1 << 25;
        if (Delay(period)) break;

        GPCR2 |= 1 << 25;
        if (Delay(period)) break;
    }

    GPCR2 |= 1 << 25;

    if (count == 0) pcf50606_power_off();

    Chat();
    restore_flags(flags);
}
    return ;
}
#endif

/////////////////////////////////////////////////////////////////////
EXTERN IDeviceDriver * CDECL CreatePowerManagers(uint_t uDeviceNo, void *pvParameter)
{
    PowerManager * pPowerManager = &powerManager;
    AddNotify(EVENT_IDLE,(void *)EnterIdleModeCallback);//for os idle thread use
    AddNotify(EVENT_IDLE_WAKEUP,(void *)CpuIdleWakeupCallback);
    AddNotify(EVENT_SLEEP,(void *)EnterSleepModeCallback);//for platform related driver use
    AddNotify(EVENT_WAKE,(void *)WakeFromSleepModeCallback);//for platform related driver use
    AddNotify(EVENT_LOCK_SCREEN,(void *)LockScreenCallback);//for noticeter use
    AddNotify(EVENT_INPUT,(void *)DM_NotifyInput);//for input event handler
#if defined(_DBGSCHIRQ)
    AddNotify(EVENT_DEBUG,(void *)DebugCallback);//for debug
#endif
    TimerCallBackProc LCD_ChangeMode = PoGetLcdChangeModePorc();
    PoSetLcdChangeModePorc(LCD_ChangeMode);
    NotifyByIsr(EVENT_INPUT, 0, NULL);//simulate an input to enable keypad and lcd power management
    if(pPowerManager)cprintf("add power manager ok\n");
    return (IDeviceDriver *)pPowerManager;
}
